#include <stdarg.h>
#include "internal_parser.h"
#include "resource.h"

namespace NS_TIEC {
  TiecodeParser::TiecodeParser(PTR<Context> context, PTR<TiecodeSource> source): context_(context), source_(source) {
    this->lexer_ = TiecodeLexer(source_->readContent());
    this->options_ = context->get<Options>(kComponentOptions);
    this->messages_ = context->get<Messages>(kComponentMessages);
  }

  PTR<CompilationUnitTree> TiecodeParser::parseCompilationUnit() {
    PTR<CompilationUnitTree> root = MAKE_PTR<CompilationUnitTree>();
    root->origin_ = currentOrigin();
    List<PTR<ClassTree>> class_defs;
    while (token_ != TiecodeToken::Eof) {
      switch (token_) {
        // 解析注释
        case TiecodeToken::LineComment:
        case TiecodeToken::LongComment:
          comment_ = lexer_.wstr();
          nextTokenSkipBlank();
          break;
        // 解析注解
        case TiecodeToken::At:
          this->annotations_ = parseAnnotationList();
          break;
        // 解析包名
        case TiecodeToken::Package:
          // 包名重复定义
          if (root->pkg_ != nullptr) {
            Position err_start = currentPosition();
            skip(false, false, false, true, false);
            syntaxErrorFromStart(err_start, "parse.package.define.duplicate");
          }
          nextTokenSkipBlank();
          root->pkg_ = parseFullName();
          break;
        default:
          nextTokenSkipBlank();
          break;
      }
    }
    return root;
  }

  PTR<StatementTree> TiecodeParser::parseStatement() {

  }

  PTR<ExpressionTree> TiecodeParser::parseExpression() {

  }

  PTR<ExpressionTree> TiecodeParser::parseType() {

  }

  PTR<CodeTree> TiecodeParser::parseCode() {
    if (!options_->enable_code_) {
      return nullptr;
    }
    return {};
  }

  List<PTR<AnnotationTree>> TiecodeParser::parseAnnotationList() {
    List<PTR<AnnotationTree>> trees;
    while (token_ == TiecodeToken::At) {
      PTR<AnnotationTree> tree = parseAnnotation();
      if (tree != nullptr) {
        trees.add(tree);
      }
      accept(TiecodeToken::Newline, "<换行>");
      if (token_ == TiecodeToken::LineComment || token_ == TiecodeToken::LongComment) {
        nextTokenSkipLine();
      }
    }
    return trees;
  }

  PTR<AnnotationTree> TiecodeParser::parseAnnotation() {
    Origin origin = currentOrigin();
    nextTokenSkipBlank();
    PTR<IdentifierTree> ident = parseName();
    if (ident->name_ == name_pool_->error_) {
      syntaxErrorFromStart(origin.range.start, "parse.expected", "<注解名称>");
      ident.reset();
      return nullptr;
    } else {
      PTR<AnnotationTree> tree = MAKE_PTR<AnnotationTree>();
      tree->origin_ = origin;
      tree->ident_ = ident;
      if (token_ == TiecodeToken::Dot) {
        Position suffix_start = currentPosition();
        nextTokenSkipBlank();
        PTR<IdentifierTree> suffix = parseName();
        if (ident->name_ == name_pool_->error_) {
          syntaxErrorFromStart(suffix_start, "parse.expected", "<注解属性后缀名>");
          suffix.reset();
        } else {
          tree->suffix_ = suffix;
        }
      }
      if (token_ == TiecodeToken::LParen) {
        PTR<ExpressionTree> arg = parseAnnotationArg();
        tree->arg_ = arg;
        accept(TiecodeToken::RParen, ")");
      }
      return tree;
    }
  }

  PTR<ExpressionTree> TiecodeParser::parseAnnotationArg() {
    accept(TiecodeToken::LParen, "(");
    pushExprMode(kExprModeInvokeArgs);
    PTR<ExpressionTree> arg = parseExpression();
    popExprMode();
    return arg;
  }

  PTR<ClassTree> TiecodeParser::parseClass() {
    PTR<ClassTree> tree = MAKE_PTR<ClassTree>();
    tree->origin_ = currentOrigin();
    accept(TiecodeToken::Class, "类");
    PTR<IdentifierTree> class_name = parseName();
    if (class_name->name_ == name_pool_->error_) {
      syntaxErrorToEnd(currentPosition().plusLength(lexer_.wsize()), "parse.expected", "类名标识符");
    }
    tree->ident_ = class_name;
    if (token_ == TiecodeToken::Lt) {
      tree->templates_ = parseClassTemplateArgs();
    }
    return tree;
  }

  List<PTR<IdentifierTree>> TiecodeParser::parseClassTemplateArgs() {

  }

  PTR<ExpressionTree> TiecodeParser::parseClassExtendsClause() {

  }

  void TiecodeParser::parseClassBody(PTR<ClassTree> klass) {

  }

  PTR<IdentifierTree> TiecodeParser::parseName() {
    if (token_ == TiecodeToken::Identifier) {
      PTR<Name> name = name_pool_->getName(lexer_.text());
      PTR<IdentifierTree> tree = MAKE_PTR<IdentifierTree>();
      tree->name_ = name;
      tree->origin_ = currentOrigin();
      Position& start = tree->origin_.range.start;
      tree->origin_.range.end = start.plusLength((UInt32)lexer_.wsize());
      nextTokenSkipBlank();
      return tree;
    }
    PTR<IdentifierTree> err_tree = MAKE_PTR<IdentifierTree>();
    err_tree->name_ = name_pool_->error_;
    err_tree->origin_ = currentOrigin();
    err_tree->origin_.range.end = err_tree->origin_.range.start;
    return err_tree;
  }

  PTR<ExpressionTree> TiecodeParser::parseFullName() {
    PTR<IdentifierTree> first = parseName();
    if (first->name_ == name_pool_->error_) {
      syntaxErrorToEnd(currentPosition().plusLength(lexer_.wsize()), "parse.expected", "标识符");
    }
    PTR<ExpressionTree> expr = first;
    while (token_ == TiecodeToken::Dot) {
      nextTokenSkipBlank();
      PTR<FieldAccessTree> tmp = MAKE_PTR<FieldAccessTree>();
      tmp->prefix_ = expr;
      tmp->ident_ = parseName();
      if (tmp->ident_->name_ == name_pool_->error_) {
        syntaxErrorToEnd(currentPosition().plusLength(lexer_.wsize()), "parse.expected", "标识符");
      }
      tmp->origin_ = {source_, {expr->origin_.range.start, tmp->ident_->origin_.range.end}};
      expr = tmp;
    }
    return expr;
  }

  void TiecodeParser::nextToken() {
    this->token_ = lexer_.lex();
    this->line_ = lexer_.lineno();
    this->column_ = lexer_.columno();
    this->index_ = lexer_.border();
  }

  void TiecodeParser::nextTokenSkipBlank() {
    this->token_ = lexer_.lex();
    while (this->token_ == TiecodeToken::Whitespace) {
      this->token_ = lexer_.lex();
    }
    this->line_ = lexer_.lineno();
    this->column_ = lexer_.columno();
    this->index_ = lexer_.border();
  }

  void TiecodeParser::nextTokenSkipLine() {
    this->token_ = lexer_.lex();
    while (this->token_ == TiecodeToken::Whitespace || this->token_ == TiecodeToken::Newline) {
      this->token_ = lexer_.lex();
    }
    this->line_ = lexer_.lineno();
    this->column_ = lexer_.columno();
    this->index_ = lexer_.border();
  }

  void TiecodeParser::accept(TiecodeToken token, const char* missed_token_msg) {
    if (this->token_ == token) {
      nextTokenSkipBlank();
      return;
    }
    syntaxErrorToEnd(currentPosition().plusLength(lexer_.wsize()), "parse.expected", missed_token_msg);
  }

  void TiecodeParser::skip(bool stop_identifier, bool stop_paren, bool stop_space, bool stop_newline, bool stop_statement) {
    while (token_ != TiecodeToken::Eof) {
      bool skip = false;
      nextToken();
      switch (token_) {
        case TiecodeToken::Identifier:
          if (stop_identifier) skip = true;
          break;
        case TiecodeToken::RParen:
          if (stop_paren) skip = true;
          break;
        case TiecodeToken::Whitespace:
          if (stop_space) skip = true;
          break;
        case TiecodeToken::Newline:
          if (stop_newline) skip = true;
          break;
        case TiecodeToken::If:
        case TiecodeToken::Switch:
        case TiecodeToken::Loop:
        case TiecodeToken::Event:
          if (stop_statement) skip = true;
          break;
      }
      if (skip) break;
    }
    return;
  }

  void TiecodeParser::syntaxErrorFromStart(Position start_pos, const char* key, ...) {
    va_list args;
    va_start(args, key);
    const char* msg = Resources::getFormatChars(key, args);
    va_end(args);
    Diagnostic diagnostic(
      source_->getUri().asString(),
      Range {start_pos, Position{line_, column_, index_}},
      key,
      msg,
      LogLevel::kLevelError
    );
    messages_->diagnostic(diagnostic);
  }

  void TiecodeParser::syntaxErrorToEnd(Position end_pos, const char* key, ...) {
    va_list args;
    va_start(args, key);
    const char* msg = Resources::getFormatChars(key, args);
    va_end(args);
    Diagnostic diagnostic(
      source_->getUri().asString(),
      Range {Position{line_, column_, index_}, end_pos},
      key,
      msg,
      LogLevel::kLevelError
    );
    messages_->diagnostic(diagnostic);
  }

  Origin TiecodeParser::currentOrigin() {
    return {source_, Range{currentPosition()}};
  }

  Position TiecodeParser::currentPosition() {
    return {line_, column_, index_};
  }

  void TiecodeParser::pushExprMode(int mode) {
    expr_mode_stack_.push(mode);
  }

  void TiecodeParser::popExprMode() {
    expr_mode_stack_.pop();
  }

  int TiecodeParser::currentExprMode() {
    if (expr_mode_stack_.empty()) {
      return kExprModeInit;
    }
    return expr_mode_stack_.top();
  }
}